package naito_rescue.agent;

import java.util.*;

import rescuecore2.worldmodel.*;
import rescuecore2.standard.messages.*;
import rescuecore2.messages.*;
import rescuecore2.log.Logger;

import rescuecore2.standard.entities.*;

import naito_rescue.*;
import naito_rescue.agent.*;
import naito_rescue.object.*;
import naito_rescue.router.*;
import naito_rescue.task.*;
import naito_rescue.task.job.*;
import naito_rescue.message.*;
import naito_rescue.message.manager.*;

/**
*  救急隊だよ
*
*/
public class NAITOAmbulanceTeam extends NAITOEnvironmentAgent<AmbulanceTeam>
{
	private Collection<StandardEntity> unexploredBuildings;
	private List<NAITOBuilding> myRescueAreas;
	private List<StandardEntity> rescueingMembers;
	private StandardEntity target_building;
	private List<EntityID> refugeIDs;
	private int            team;
	private AmbulanceTeam  me;
	private boolean        isPreferredVoice;
	private boolean        isPreferredNear;
	private List<StandardEntity> refuges;
	private List<StandardEntity> targetsByMessage;
	private double rescueingWidth;
	private double rescueingHeight;
	private double rescueingStartX;
	private double rescueingStartY;
	private List<EntityID> targetBuildingIDs;
	
	
	protected void postConnect(){
       super.postConnect();
		model.indexClass(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM, StandardEntityURN.REFUGE, StandardEntityURN.BUILDING);
		unexploredBuildings = model.getEntitiesOfType(StandardEntityURN.BUILDING);
		
		refuges = new ArrayList<StandardEntity>();
		for(StandardEntity refuge : model.getEntitiesOfType(StandardEntityURN.REFUGE)){
			refuges.add(refuge);
		}
		this.targetsByMessage = new ArrayList<StandardEntity>();
		this.rescueingMembers = new ArrayList<StandardEntity>();
		this.rescueingMembers.addAll(this.atList);
		this.myRescueAreas = new ArrayList<NAITOBuilding>();
		
		this.refugeIDs = new ArrayList<EntityID>();
		for(StandardEntity refuge : refuges){
			refugeIDs.add(refuge.getID());
		}
		this.targetBuildingIDs = new ArrayList<EntityID>();
	}
	private void decideRescueingArea(){
		int    roleID = rescueingMembers.indexOf(me());
		int    oldRoleID = roleID;
		int    separateBlock = 1;
		
		
		//DisasterSpaceをいくつのブロックに分割するかを決める
		for(; (separateBlock * separateBlock) < rescueingMembers.size();separateBlock++);

		separateBlock--;
		if(separateBlock < 1) separateBlock = 1;

		//roleIDの正規化...roleID=[0 ... pow(separateBlock,2)-1]になるように
		while(roleID >= (separateBlock * separateBlock)) roleID -= separateBlock;
		
		String context = "decideRescueingArea";
		//logger.debug("rescueingMembers.size() = " + rescueingMembers.size(), context);
		//logger.debug("roleID = " + oldRoleID, context);
		//logger.debug("separateBlock = " + separateBlock, context);
		//logger.debug("Normalized roleID = " + roleID, context);
		//logger.debug("w_minX = " + w_minX, context);
		//logger.debug("w_minY = " + w_minY, context);
		//logger.debug("w_maxX = " + w_maxX, context);
		//logger.debug("w_maxY = " + w_maxY, context);
		//どっからどこまでのBuildingを探訪するか決定する
		rescueingWidth  = (w_maxX - w_minX) / separateBlock;
		rescueingHeight = (w_maxY - w_minY) / separateBlock;
		rescueingStartX      = w_minX + rescueingWidth * (roleID % separateBlock);
		rescueingStartY      = w_minY + rescueingHeight * (roleID / separateBlock);
		
		//logger.debug("rescueingWidth = " + rescueingWidth, context);
		//logger.debug("rescueingHeight = " + rescueingHeight, context);
		//logger.debug("rescueingStartX = " + rescueingStartX, context);
		//logger.debug("rescueingStartY = " + rescueingStartY, context);
		Building b = null;
		for(StandardEntity building : allBuildings){
			b = (Building)building;
			if(b.getX() > rescueingStartX && b.getX() <= (rescueingStartX + rescueingWidth) &&
			   b.getY() > rescueingStartY && b.getY() <= (rescueingStartY + rescueingHeight)){
				myRescueAreas.add(allNAITOBuildings.get(b.getID()));
				//logger.debug("clearAroundHeres.add(ID=" + b.getID() + ")", context);
			}
		}
	}
	public String toString(){
		return "NAITOAmbulanceTeam." + me().getID() + "";
	}

	
	protected void think(int time, ChangeSet changed, Collection<Command> heard){
		super.think(time, changed, heard);
		String context = "NAITOAmbulanceTeam#think";
		if (time < config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)){
			return;
		}
		if (time == config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)) {

			if(isNeedRescue()){
				//rescue(me().getID());
				return;
			}
		}
		removeTargetID();
        // Am I transporting a civilian to a refuge?
        if (someoneOnBoard() != null) {
            // Am I at a refuge?
            if (location() instanceof Refuge) {
                // Unload!
                sendUnload(time);
                return;
            }
            else {
                // Move to a refuge
                List<EntityID> path = search.backwardAStarSearch(refugeIDs);
                if (path != null) {
                    sendMove(time, path);
                    return;
                }
                // What do I do now? Might as well carry on and see if we can dig someone else out.
            }
        }
        // Go through targets (sorted by distance) and check for things we can do
        for (Human next : getTargets()) {
            if (next.getPosition().equals(location().getID())) {
                // Targets in the same place might need rescueing or loading
                if ((next instanceof Civilian) && next.getBuriedness() == 0 && !(location() instanceof Refuge)) {
                    // Load
                    sendLoad(time, next.getID());
                    return;
                }
                if (next.getBuriedness() > 0) {
                    // Rescue
                    sendRescue(time, next.getID());
                    return;
                }
            }
            else {
                // Try to move to the target
                List<EntityID> path = search.backwardAStarSearch(next.getPosition());
                if (path != null) {
                    move(path);
                    return;
                }
            }
		}
		/* フィルタリングされたメッセージから，次に移動すべき建物を決定する */
		for(NAITOMessage message : this.filteredMessages){
			// フィルタリングされたHelpMeInBuildingMessageから，建物のIDを取得
			HelpMeInBuildingMessage help = (HelpMeInBuildingMessage)message;
			targetBuildingIDs.add(help.getBuildingID());
		}
		addTargetIfNew(targetBuildingIDs);
		if(!this.targetsByMessage.isEmpty()){
			// 次に移動すべき建物があるならば，経路探索をして移動する
			StandardEntity target = selectRescueTarget(); //次に移動すべき建物の中で，距離の一番近い建物を取得
			List<EntityID> path = search.backwardAStarSearch(target.getID());
			if(path != null){
				move(path);
				return;
			}
		}
		doDefaultSearch();
	}
	private void removeTargetID(){
		EntityID locationID = getLocation().getID();
		if(targetBuildingIDs != null && !targetBuildingIDs.isEmpty()){
			for(EntityID id : targetBuildingIDs){
				if(locationID.getValue() == id.getValue()){
					targetBuildingIDs.remove(id);
				}
			}
		}
	}
	private StandardEntity selectRescueTarget(){
		List<StandardEntity> targets = new ArrayList<StandardEntity>();
		boolean isIncludedMyArea = false;
		for(StandardEntity entity : targetsByMessage){
			Building b = (Building)entity;
			if(b.getX() > rescueingStartX && b.getX() <= (rescueingStartX + rescueingWidth) &&
			   b.getY() > rescueingStartY && b.getY() <= (rescueingStartY + rescueingHeight)){
				isIncludedMyArea = true;
				targets.add(entity);
				//logger.debug("clearAroundHeres.add(ID=" + b.getID() + ")", context);
			}
		}
		if(!isIncludedMyArea){
			targets.addAll(targetsByMessage);
		}
		/*
		for(Iterator<StandardEntity> iterator = targets.iterator();iterator.hasNext();){
			StandardEntity entity = iterator.next();
			List<EntityID> path = search.breadthFirstSearch(getLocation(), entity);
			if(path == null){
				iterator.remove();
			}
		}
		*/
		Collections.sort(targets, new DistanceSorter(getLocation(), this.model));
		//logger.debug("targets=" + targets, "selectRescueTarget");
		if(!targets.isEmpty()){
			return targets.get(0);
		}else{
			return null;
		}
	}
	private void addTargetIfNew(List<EntityID> IDs){
		for(EntityID id : IDs){
			StandardEntity building = model.getEntity(id);
			if(!targetsByMessage.contains(building)){
				targetsByMessage.add(building);
			}
		}
	}
	protected void updateExploredTarget(){
		if(!targetsByMessage.isEmpty()){
			Area location = (Area)(getLocation());
			for(Iterator<StandardEntity> iterator = targetsByMessage.iterator();iterator.hasNext();){
				StandardEntity entity = iterator.next();
				if(location.getID().getValue() == entity.getID().getValue()){
					//logger.debug("Remove area has visited to rescue.", "NAITOAmbulanceTeam#updateExploredTarget");
					iterator.remove();
				}
			}	
		}
	}
    protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() {
		return EnumSet.of(StandardEntityURN.AMBULANCE_TEAM);
	}
    private List<Human> getTargets() {
        List<Human> targets = new ArrayList<Human>();
        for (StandardEntity next : model.getEntitiesOfType(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM)) {
            Human h = (Human)next;
            if (h == me()) {
                continue;
            }
            if (h.isHPDefined()
                && h.isBuriednessDefined()
                && h.isDamageDefined()
                && h.isPositionDefined()
                && h.getHP() > 0
                && (h.getBuriedness() > 0 || h.getDamage() > 0)
				&& h.getBuriedness() > 0){
                
                targets.add(h);
            }
        }
        Collections.sort(targets, new DistanceSorter(location(), model));
        return targets;
    }

}
